// exception.cc 
//	Entry point into the Nachos kernel from user programs.
//	There are two kinds of things that can cause control to
//	transfer back to here from user code:
//
//	syscall -- The user code explicitly requests to call a procedure
//	in the Nachos kernel.  Right now, the only function we support is
//	"Halt".
//
//	exceptions -- The user code does something that the CPU can't handle.
//	For instance, accessing memory that doesn't exist, arithmetic errors,
//	etc.  
//
//	Interrupts (which can also cause control to transfer from user
//	code into the Nachos kernel) are handled elsewhere.
//
// For now, this only handles the Halt() system call.
// Everything else core dumps.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "system.h"
#include "syscall.h"
#include <stdio.h>
#include <iostream>
#ifdef CHANGED
#include "../threads/synch.h"

#ifdef NETWORK
#include "../network/netcall.h"
#endif

#define MAX_USER_LOCK 200 //max # of locks a user can create
#define MAX_USER_CV 400   //max # of CVs a user can create
#define MAX_USER_MV 800   //max # of MVs a user can create

Lock* userLocks[MAX_USER_LOCK];
Condition* userCV[MAX_USER_CV];
int* userMV[MAX_USER_MV];
AddrSpace* lockProcessMap[MAX_USER_LOCK];
AddrSpace* cvProcessMap[MAX_USER_CV];

Lock* exitLock = new Lock ("Exit Lock");
Lock* forkLock = new Lock ("Fork Lock");
Lock* execLock = new Lock ("Exec Lock");
Lock* copyInLock = new Lock ("Copy In Lock");
Lock* iptAccessLock = new Lock ("IPT Access Lock");
Lock* processAccessLock = new Lock ("Process Access Lock");
Lock* execAccessLock = new Lock ("Exec Access Lock");
Lock* swapAccessLock = new Lock ("Swap Access Lock");

Lock* createLockLock = new Lock("CreateLock Lock");
Lock* acquireLock = new Lock("Acquire Lock");
Lock* releaseLock = new Lock("Release Lock");
Lock* destroyLockLock = new Lock("DestroyLock Lock");
Lock* checkLock = new Lock("CheckLock Lock");
Lock* createCVLock = new Lock("CreateCVLock Lock");
Lock* signalLock = new Lock("Signal Lock");
Lock* broadcastLock = new Lock("Broadcast Lock");
Lock* waitLock = new Lock("Wait Lock");
Lock* destroyCVLock = new Lock("Destroy Lock");
Lock* yieldLock = new Lock("Yield Lock");
Lock* createMVLock = new Lock("CreateMV Lock");
Lock* destroyMVLock = new Lock("DestroyMV Lock");
Lock* getMVLock = new Lock("GetMV Lock");
Lock* setMVLock = new Lock("SetMV Lock");

int createLockIndex = 0;
int createCVIndex = 0;
int createMVIndex = 0;

int globalEviction=-1;

/*Assigning mails IDs. Whenever exec is called, process id stored.
* When time to send a Request to server, search array for process ID
* and then use index as mailID.*/
AddrSpace* processIDs[10]; /*Up to ten user processes a nachos instance. Each Post Office set to have only 10 mailboxes*/
int procIDIndex = 0;

void assignMailID(AddrSpace* spaceID);
int getMailID();

#endif/*CHANGED*/

using namespace std;

int copyin(unsigned int vaddr, int len, char *buf) {
    // Copy len bytes from the current thread's virtual address vaddr.
    // Return the number of bytes so read, or -1 if an error occors.
    // Errors can generally mean a bad virtual address was passed in.
	copyInLock->Acquire();
    bool result;
    int n=0;			// The number of bytes copied in
    int *paddr = new int;

    while ( n >= 0 && n < len) {
      result = machine->ReadMem( vaddr, 1, paddr );
      while(!result) // FALL 09 CHANGES
	  {
   			result = machine->ReadMem( vaddr, 1, paddr ); // FALL 09 CHANGES: TO HANDLE PAGE FAULT IN THE ReadMem SYS CALL
	  }	
      
      buf[n++] = *paddr;
     
      if ( !result ) {
	//translation failed
	return -1;
      }

      vaddr++;
    }

    delete paddr;
	copyInLock->Release();
    return len;
}

int copyout(unsigned int vaddr, int len, char *buf) {
    // Copy len bytes to the current thread's virtual address vaddr.
    // Return the number of bytes so written, or -1 if an error
    // occors.  Errors can generally mean a bad virtual address was
    // passed in.
    bool result;
    int n=0;			// The number of bytes copied in

    while ( n >= 0 && n < len) {
      // Note that we check every byte's address
      result = machine->WriteMem( vaddr, 1, (int)(buf[n++]) );

      if ( !result ) {
	//translation failed
	return -1;
      }

      vaddr++;
    }

    return n;
}

void Create_Syscall(unsigned int vaddr, int len) {
    // Create the file with the name in the user buffer pointed to by
    // vaddr.  The file name is at most MAXFILENAME chars long.  No
    // way to return errors, though...
    char *buf = new char[len+1];	// Kernel buffer to put the name in

    if (!buf) return;

    if( copyin(vaddr,len,buf) == -1 ) {
	printf("%s","Bad pointer passed to Create\n");
	delete buf;
	return;
    }

    buf[len]='\0';

    fileSystem->Create(buf,0);
    delete[] buf;
    return;
}

int Open_Syscall(unsigned int vaddr, int len) {
    // Open the file with the name in the user buffer pointed to by
    // vaddr.  The file name is at most MAXFILENAME chars long.  If
    // the file is opened successfully, it is put in the address
    // space's file table and an id returned that can find the file
    // later.  If there are any errors, -1 is returned.
    char *buf = new char[len+1];	// Kernel buffer to put the name in
    OpenFile *f;			// The new open file
    int id;				// The openfile id

    if (!buf) {
	printf("%s","Can't allocate kernel buffer in Open\n");
	return -1;
    }

    if( copyin(vaddr,len,buf) == -1 ) {
	printf("%s","Bad pointer passed to Open\n");
	delete[] buf;
	return -1;
    }

    buf[len]='\0';

    f = fileSystem->Open(buf);
    delete[] buf;

    if ( f ) {
	if ((id = currentThread->space->fileTable.Put(f)) == -1 )
	    delete f;
	return id;
    }
    else
	return -1;
}

void Write_Syscall(unsigned int vaddr, int len, int id) {
    // Write the buffer to the given disk file.  If ConsoleOutput is
    // the fileID, data goes to the synchronized console instead.  If
    // a Write arrives for the synchronized Console, and no such
    // console exists, create one. For disk files, the file is looked
    // up in the current address space's open file table and used as
    // the target of the write.
    
    char *buf;		// Kernel buffer for output
    OpenFile *f;	// Open file for output

    if ( id == ConsoleInput) return;
    
    if ( !(buf = new char[len]) ) {
	printf("%s","Error allocating kernel buffer for write!\n");
	return;
    } else {
        if ( copyin(vaddr,len,buf) == -1 ) {
	    printf("%s","Bad pointer passed to to write: data not written\n");
	    delete[] buf;
	    return;
	}
    }

    if ( id == ConsoleOutput) {
      for (int ii=0; ii<len; ii++) {
	printf("%c",buf[ii]);
      }

    } else {
	if ( (f = (OpenFile *) currentThread->space->fileTable.Get(id)) ) {
	    f->Write(buf, len);
	} else {
	    printf("%s","Bad OpenFileId passed to Write\n");
	    len = -1;
	}
    }

    delete[] buf;
}

int Read_Syscall(unsigned int vaddr, int len, int id) {
    // Write the buffer to the given disk file.  If ConsoleOutput is
    // the fileID, data goes to the synchronized console instead.  If
    // a Write arrives for the synchronized Console, and no such
    // console exists, create one.    We reuse len as the number of bytes
    // read, which is an unnessecary savings of space.
    char *buf;		// Kernel buffer for input
    OpenFile *f;	// Open file for output

    if ( id == ConsoleOutput) return -1;
    
    if ( !(buf = new char[len]) ) {
	printf("%s","Error allocating kernel buffer in Read\n");
	return -1;
    }

    if ( id == ConsoleInput) {
      //Reading from the keyboard
      scanf("%s", buf);

      if ( copyout(vaddr, len, buf) == -1 ) {
	printf("%s","Bad pointer passed to Read: data not copied\n");
      }
    } else {
	if ( (f = (OpenFile *) currentThread->space->fileTable.Get(id)) ) {
	    len = f->Read(buf, len);
	    if ( len > 0 ) {
	        //Read something from the file. Put into user's address space
  	        if ( copyout(vaddr, len, buf) == -1 ) {
		    printf("%s","Bad pointer passed to Read: data not copied\n");
		}
	    }
	} else {
	    printf("%s","Bad OpenFileId passed to Read\n");
	    len = -1;
	}
    }

    delete[] buf;
    return len;
}

void Close_Syscall(int fd) {
    // Close the file associated with id fd.  No error reporting.
    OpenFile *f = (OpenFile *) currentThread->space->fileTable.Remove(fd);

    if ( f ) {
      delete f;
    } else {
      printf("%s","Tried to close an unopen file\n");
    }
}
#ifdef CHANGED

void Reset_TLB() // on context switch
{
	tlbLock->Acquire();
	for (int i = 0; i<TLBSize; i++)
	{
		if(machine->tlb[i].valid && machine->tlb[i].dirty)
			ipt->MakeDirty(machine->tlb[i].physicalPage);
		machine->tlb[i].valid = FALSE;
	}
	tlbLock->Release();
}

void Yield_Syscall(int printMsg) {
	yieldLock->Acquire();
    // Yield current thread
	Reset_TLB();
	if(printMsg)
	{
		printf("I am yielding!!!!!\n");
	}
	currentThread->Yield();
	yieldLock->Release();
}

int CreateLock_Syscall(unsigned int vaddr, int len, int printMsg) {
	char* name;
	if ( !(name = new char[len]) ) {
		printf("%s","Error allocating kernel buffer for saving lock name!\n");
		return -1;
	} 
	else {
		if ( copyin(vaddr,len,name) == -1 ) {
			printf("%s","Bad pointer passed to CreateLock: data not written\n");
			delete[] name;
			return -1;
		}
	}
#ifndef NETWORK //If not using server to handle creation
    // CreateLock
	createLockLock->Acquire();
    //If index is in bounds, create a lock in the lock array
    if((createLockIndex >= 0)&&(createLockIndex < (MAX_USER_LOCK-1)))
    {
            userLocks[createLockIndex] = new Lock(name);
			lockProcessMap[createLockIndex] = currentThread->space;
			if(printMsg)
			{
				if(userLocks[createLockIndex] != NULL)
				{
					printf("%s created successfully!\n", name);
				}
				else
				{
					printf("%s NOT created successfully NOOOO!\n", name);
				}
			}
			createLockLock->Release();
            return createLockIndex++;
    }
	else
	{
		if(printMsg)
		{
			printf("Lock cannot be created! Maximum number of locks [%d] exceeded!\n", MAX_USER_LOCK);
		}
		createLockLock->Release();
		return -1;
	}
#else
	//printf("Running from network folder!\n");
	int lockIndex = Request(CREATELOCK, name, getMailID()); //Request that the server create a lock 
	if(lockIndex > -1) //If valid index. Not checking MAX because that is information decided by server. 
	{
		printf("%s created at index %d successfully or already existed!\n", name, lockIndex); 
		return lockIndex;
	}
	else
	{
		printf("%s NOT created successfully!\n", name);
		return -1;
	}
#endif
}

int Acquire_Syscall(int lockIndex, int printMsg) {
#ifndef NETWORK //If not using server to handle acquiring lock
	acquireLock->Acquire();
	//printMsg = true;
    // Acquire a lock from array of user locks

	int error = 0; //0 - success, 1 - Desired lock does not exist
				   //3 - lockIndex out of bounds, 5 - Lock belongs to a different process

	if((lockIndex < 0)||(lockIndex > (MAX_USER_LOCK-1)))
	{
		error = 3; 
		if(printMsg)
		{
			printf("Cannot acquire: Lock index [%d] out of bounds [%d, %d].\n", lockIndex, 0, (MAX_USER_LOCK-1));
		}
		acquireLock->Release();
		return error;
	}

	if(userLocks[lockIndex] != NULL)
	{
		if(lockProcessMap[lockIndex] != currentThread->space)
		{
			error = 5; 
			if(printMsg)
			{
				printf("Cannot acquire: %s belongs to different process.\n", userLocks[lockIndex]->getName());
			}
			acquireLock->Release();
			return error;
		}

		userLocks[lockIndex]->Acquire();
		if(printMsg)
		{
			printf("%s acquired successfully!\n", userLocks[lockIndex]->getName());
		}
	}
	else
	{
		error = 1; 
		if(printMsg)
		{
			printf("Cannot acquire: Lock does not exist at index %d.\n", lockIndex);
		}
	}
	acquireLock->Release();
	return error; //Return if failed or not
#else
	char* indexBuf;
	if ( !(indexBuf = new char[16]) ) {
		printf("%s","Error allocating kernel buffer for saving index!\n");
		return -1;
	} 
    //sprintf(name,"Lock:%d",lockIndex); //Change back when remove prefix in netcall.cc
	sprintf(indexBuf,"%d",lockIndex);
	//printf("Running from network folder!\n");
	int acquiredIndex = Request(ACQUIRE, indexBuf, getMailID()); //Request that the server aqcuire a lock 
	if(acquiredIndex == lockIndex) //If lock acquired is the one client asked to acquire
	{
		printf("Lock at index %d acquired successfully!\n", acquiredIndex);
		return 0; //0 - error code for success
	}
	else
	{
		return -1;
	}
#endif
}

int Release_Syscall(int lockIndex, int printMsg) {
#ifndef NETWORK //If not using server to handle releasing lock
    // Release a lock from array of user locks
	releaseLock->Acquire();
	int error = 0; //0 - success, 1 - Desired lock does not exist
				   //3 - lockIndex out of bounds, 5 - Lock belongs to a different process

	if((lockIndex < 0)||(lockIndex > (MAX_USER_LOCK-1)))
	{
		error = 3; 
		if(printMsg)
		{
			printf("Cannot release: Lock index [%d] out of bounds [%d, %d].\n", lockIndex, 0, (MAX_USER_LOCK-1));
		}
		releaseLock->Release();
		return error;
	}

	if(userLocks[lockIndex] != NULL)
	{
		if(lockProcessMap[lockIndex] != currentThread->space)
		{
			error = 5; 
			if(printMsg)
			{
				printf("Cannot release: %s belongs to different process.\n", userLocks[lockIndex]->getName());
			}
			releaseLock->Release();
			return error;
		}

		userLocks[lockIndex]->Release();
		if(printMsg)
		{
			printf("%s released successfully!\n", userLocks[lockIndex]->getName());
		}
	}
	else
	{
		error = 1; 
		if(printMsg)
		{
			printf("Cannot release: Lock does not exist at index %d.\n", lockIndex);
		}

	}
	releaseLock->Release();
	return error; //Return if failed or not
#else
	char* indexBuf = new char [16];
	sprintf(indexBuf,"%d",lockIndex);
	//printf("Running from network folder!\n");
	int releasedIndex = Request(RELEASE, indexBuf, getMailID()); //Request that the server create a lock 
	if(releasedIndex == lockIndex) //If lock released is the one client asked to release
	{
		printf("Lock at index %d released successfully!\n", releasedIndex);
		return 0; //0 - error code for success
	}
	else
	{
		return -1;
	}
#endif
}

int DestroyLock_Syscall(int lockIndex, int printMsg) {
#ifndef NETWORK //If not using server to handle destruction
    // Destroy a lock from array of user locks
	destroyLockLock->Acquire();
	int error = 0; //0 - success, 1 - Desired lock does not exist
				   //3 - lockIndex out of bounds, 5 - Lock belongs to a different process

	if((lockIndex < 0)||(lockIndex > (MAX_USER_LOCK-1)))
	{
		error = 3; 
		if(printMsg)
		{
			printf("Cannot be destroyed: Lock index [%d] out of bounds [%d, %d].\n", lockIndex, 0, (MAX_USER_LOCK-1));
		}
		destroyLockLock->Release();
		return error;
	}

	if(userLocks[lockIndex] != NULL)
	{	
		if(lockProcessMap[lockIndex] != currentThread->space)
		{
			error = 5; 
			if(printMsg)
			{
				printf("Cannot destroy: %s belongs to different process.\n", userLocks[lockIndex]->getName());
			}
			destroyLockLock->Release();
			return error;
		}

		delete userLocks[lockIndex];
		userLocks[lockIndex] = NULL;

		if(userLocks[lockIndex] == NULL)
		{
			if(printMsg)
			{
				printf("Lock_%d destroyed successfully!\n", lockIndex);
			}
		}
		else
		{
			printf("Error in destruction of Lock_%d!\n", lockIndex);
		}
	}
	else 
	{
		error = 1; 
		if(printMsg)
		{
			printf("Cannot be destroyed: Lock does not exist at index %d.\n", lockIndex);
		}
	}
	destroyLockLock->Release();
	return error; //Return if failed or not
#else
	char* indexBuf;
	if ( !(indexBuf = new char[16]) ) {
		printf("%s","Error allocating kernel buffer for saving index!\n");
		return -1;
	} 
    
	sprintf(indexBuf,"%d",lockIndex);
	//printf("Running from network folder!\n");
	int destroyIndex = Request(DESTROYLOCK, indexBuf, getMailID()); //Request that the server aqcuire a lock 
	
	if(destroyIndex == lockIndex) //If lock destroyed is the one client asked to destroy
	{
		printf("Lock at index %d destroyed successfully!\n", destroyIndex);
		return 0; //0 - error code for success
	}
	else
	{
		return -1;
	} 
#endif
}

int CheckLock_Syscall(int lockIndex, int printMsg) {
    // Check if lock is valid from array of user locks
	checkLock->Acquire();
	int error = 0; //0 - success, 1 - Desired lock does not exist
				   //3 - lockIndex out of bounds
	//printf("1 Start checking!\n");
	if((lockIndex < 0)||(lockIndex > (MAX_USER_LOCK-1)))
	{
		error = 3; 
		if(printMsg)
		{
			//printf("Lock index [%d] out of bounds [%d, %d].\n", lockIndex, 0, (MAX_USER_LOCK-1));
		}
		checkLock->Release();
		return error;
	}
	//printf("2 Start checking! Lock:%d\n", lockIndex);
	//printf("userLocks[lockIndex] is %d\n",userLocks[lockIndex]);
	if(userLocks[lockIndex] != NULL)
	{	
		if(printMsg)
		{
			//printf("%s is NOT NULL!\n", userLocks[lockIndex]->getName());
		}
	}
	else 
	{
		error = 1; 
		if(printMsg)
		{
			//printf("Lock does not exist at index %d.\n", lockIndex);
		}
	}
	//printf("3 Start checking!\n");
	checkLock->Release();
	return error; //Return if failed or not
}

int CreateCV_Syscall(unsigned int vaddr, int len, int printMsg) {
#ifdef NETWORK //If using network server codes
	createCVLock->Acquire();
	
	char* name;
	if ( !(name = new char[len]) ) {
		printf("%s","Error allocating kernel buffer for Create CV name!\n");
		createCVLock->Release();
		return -1;
	} 
	else {
		if ( copyin(vaddr,len,name) == -1 ) {
			printf("%s","Bad pointer passed to CreateLock: data not written\n");
			delete[] name;
			createCVLock->Release();
			return -1;
		}
	}
	//return Request(CREATECV, name);
	int success = Request(CREATECV, name, getMailID()); //Request that the server create a lock 
	if(success > -1) 
	{
		printf("%s created at index %d successfully!\n", name, success);
		return success;
	}
	else
	{
		printf("%s created Failed!\n", name);
		return -1;
	}
#else
    // CreateCV
	createCVLock->Acquire();
	//If index is in bounds, create a lock in the lock array
    if((createCVIndex >= 0)&&(createCVIndex <= (MAX_USER_CV-1)))
    {
            char* name = new char [16];
            sprintf(name,"CV_%d",createCVIndex);
            userCV[createCVIndex] = new Condition(name);
			cvProcessMap[createCVIndex] = currentThread->space;
			if(printMsg)
			{
				printf("%s created successfully!\n", name);
			}
			createCVLock->Release();
            return createCVIndex++;
    }
    else
    {
		if(printMsg)
		{
            printf("CV cannot be created! Maximum number of CVs [%d] exceeded!\n", MAX_USER_CV);
		}
		createCVLock->Release();
        return -1;
    }
#endif
}

int Signal_Syscall(int cvIndex, int lockIndex, int printMsg) {
#ifdef NETWORK //If using network server codes
	char* data;
	if ( !(data = new char[16]) ) {
		printf("%s","Error allocating kernel buffer for saving index!\n");
		return -1;
	} 
	sprintf(data,"%d_%d", cvIndex, lockIndex);
	
	int success = Request(SIGNAL, data, getMailID()); //Request that the server
	if(success == cvIndex) 
	{
		printf("Signal runs successfully!\n");
		return 0;
	}
	else
	{
		return -1;
	}
#else
	//printMsg = true;
    // Signal a cv from array of user CVs, using a lock from array of locks
	signalLock->Acquire();
	int error = 0; //0 - success, 1 - Desired lock does not exist, 2 - Desired CV does not exist
				   //3 - lockIndex out of bounds, 4 - cvIndex out of bounds, 
				   //5 - Lock belongs to a different process, 6 - CV belongs to a different process

	if((lockIndex < 0)||(lockIndex > (MAX_USER_LOCK-1)))
	{
		error = 3; 
		if(printMsg)
		{
			printf("Cannot signal: Lock index [%d] out of bounds [%d, %d].\n", lockIndex, 0, (MAX_USER_LOCK-1));
		}
		signalLock->Release();
		return error;
	}

	if((cvIndex < 0)||(cvIndex > (MAX_USER_CV-1)))
	{
		error = 4; 
		if(printMsg)
		{
			printf("Cannot signal: CV index [%d] out of bounds [%d, %d].\n", cvIndex, 0, (MAX_USER_CV-1));
		}
		signalLock->Release();
		return error;
	}

	if(userLocks[lockIndex] != NULL)
	{
		if(lockProcessMap[lockIndex] != currentThread->space)
		{
			error = 5; 
			if(printMsg)
			{
				printf("Cannot signal: %s belongs to different process.\n", userLocks[lockIndex]->getName());
			}
			signalLock->Release();
			return error;
		}

		if(userCV[cvIndex] != NULL)
		{	
			if(cvProcessMap[cvIndex] != currentThread->space)
			{
				error = 6; 
				if(printMsg)
				{
					printf("Cannot signal: %s belongs to different process.\n", userCV[cvIndex]->getName());
				}
				signalLock->Release();
				return error;
			}

			userCV[cvIndex]->Signal(userLocks[lockIndex]);
			if(printMsg)
			{
				printf("%s signalled successfully using %s!\n", userCV[cvIndex]->getName(), userLocks[lockIndex]->getName());
			}
		}
		else
		{
			error = 2; 
			if(printMsg)
			{
				printf("Cannot signal: CV does not exist at index %d.\n", cvIndex);
			}
		}
	}
	else
	{
		error = 1; 
		if(printMsg)
		{
			printf("Cannot signal: Lock does not exist at index %d.\n", lockIndex);
		}
	}
	signalLock->Release();
	return error; //Return if failed or not
#endif
}

int Wait_Syscall(int cvIndex, int lockIndex, int printMsg) {
#ifdef NETWORK //If using network server codes
	char* data;
	if ( !(data = new char[16]) ) {
		printf("%s","Error allocating kernel buffer for saving index!\n");
		return -1;
	} 
	sprintf(data,"%d_%d", cvIndex, lockIndex);
	
	int success = Request(WAIT, data, getMailID()); //Request that the server
	if(success == cvIndex) 
	{
		printf("Wait runs successfully!\n");
		return 0;
	}
	else
	{
		return -1;
	}
#else
	//printMsg=1;
    // Wait on a cv from array of user CVs, using a lock from array of locks
	waitLock->Acquire();
	//printf("%d Wait lock %d acquired!!!\n", cvIndex, lockIndex);
	int error = 0; //0 - success, 1 - Desired lock does not exist, 2 - Desired CV does not exist
				   //3 - lockIndex out of bounds, 4 - cvIndex out of bounds, 
				   //5 - Lock belongs to a different process, 6 - CV belongs to a different process

	if((lockIndex < 0)||(lockIndex > (MAX_USER_LOCK-1)))
	{
		error = 3;
		if(printMsg)
		{
			printf("Cannot wait: Lock index [%d] out of bounds [%d, %d].\n", lockIndex, 0, (MAX_USER_LOCK-1));
		}
		waitLock->Release();
		return error;
	}

	if((cvIndex < 0)||(cvIndex > (MAX_USER_CV-1)))
	{
		error = 4; 
		if(printMsg)
		{
			printf("Cannot wait: CV index [%d] out of bounds [%d, %d].\n", cvIndex, 0, (MAX_USER_CV-1));
		}
		waitLock->Release();
		return error;
	}

	if(userLocks[lockIndex] != NULL)
	{
		if(lockProcessMap[lockIndex] != currentThread->space)
		{
			error = 5; 
			if(printMsg)
			{
				printf("Cannot wait: %s belongs to different process.\n", userLocks[lockIndex]->getName());
			}
			waitLock->Release();
			return error;
		}

		if(userCV[cvIndex] != NULL)
		{	
			if(cvProcessMap[cvIndex] != currentThread->space)
			{
				error = 6; 
				if(printMsg)
				{
					printf("Cannot wait: %s belongs to different process.\n", userCV[cvIndex]->getName());
				}
				waitLock->Release();
				return error;
			}

			waitLock->Release();
			userCV[cvIndex]->Wait(userLocks[lockIndex]);
			if(printMsg)
			{
				printf("%s waited successfully using %s!\n", userCV[cvIndex]->getName(), userLocks[lockIndex]->getName());
			}
		}
		else
		{
			error = 2; 
			if(printMsg)
			{
				printf("Cannot wait: CV does not exist at index %d.\n", cvIndex);
			}
			waitLock->Release();
		}
	}
	else
	{
		error = 1; 
		if(printMsg)
		{
			printf("Cannot wait: Lock does not exist at index %d.\n", lockIndex);
		}
		waitLock->Release();
	}
	return error; //Return if failed or not
#endif
}

int Broadcast_Syscall(int cvIndex, int lockIndex, int printMsg) {
#ifdef NETWORK //If using network server codes
	char* data;
	if ( !(data = new char[16]) ) {
		printf("%s","Error allocating kernel buffer for saving index!\n");
		return -1;
	} 
	sprintf(data,"%d_%d", cvIndex, lockIndex);
	
	int success = Request(BROADCAST, data, getMailID()); //Request that the server
	if(success == cvIndex) 
	{
		printf("Broadcast runs successfully!\n");
		return 0;
	}
	else
	{
		return -1;
	}
#else
    // Broadcast a cv from array of user CVs, using a lock from array of locks
	broadcastLock->Acquire();
	int error = 0; //0 - success, 1 - Desired lock does not exist, 2 - Desired CV does not exist
				   //3 - lockIndex out of bounds, 4 - cvIndex out of bounds
				   //5 - Lock belongs to a different process, 6 - CV belongs to a different process

	if((lockIndex < 0)||(lockIndex > (MAX_USER_LOCK-1)))
	{
		error = 3; 
		if(printMsg)
		{
			printf("Cannot broadcast: Lock index [%d] out of bounds [%d, %d].\n", lockIndex, 0, (MAX_USER_LOCK-1));
		}
		broadcastLock->Release();
		return error;
	}

	if((cvIndex < 0)||(cvIndex > (MAX_USER_CV-1)))
	{
		error = 4; 
		if(printMsg)
		{
			printf("Cannot broadcast: CV index [%d] out of bounds [%d, %d].\n", cvIndex, 0, (MAX_USER_CV-1));
		}
		broadcastLock->Release();
		return error;
	}

	if(userLocks[lockIndex] != NULL)
	{
		if(lockProcessMap[lockIndex] != currentThread->space)
		{
			error = 5; 
			if(printMsg)
			{
				printf("Cannot broadcast: %s belongs to different process.\n", userLocks[lockIndex]->getName());
			}
			broadcastLock->Release();
			return error;
		}

		if(userCV[cvIndex] != NULL)
		{	
			if(cvProcessMap[cvIndex] != currentThread->space)
			{
				error = 6; 
				if(printMsg)
				{
					printf("Cannot broadcast: %s belongs to different process.\n", userCV[cvIndex]->getName());
				}
				broadcastLock->Release();
				return error;
			}

			userCV[cvIndex]->Broadcast(userLocks[lockIndex]);
			if(printMsg)
			{
				printf("%s broadcasted successfully using %s!\n", userCV[cvIndex]->getName(), userLocks[lockIndex]->getName());
			}
		}
		else
		{
			error = 2; 
			if(printMsg)
			{
				printf("Cannot broadcast: CV does not exist at index %d.\n", cvIndex);
			}
		}
	}
	else
	{
		error = 1; 

		if(printMsg)
		{
			printf("Cannot broadcast: Lock does not exist at index %d.\n", lockIndex);
		}
	}
	broadcastLock->Release();
	return error; //Return if failed or not
#endif
}

int DestroyCV_Syscall(int cvIndex, int printMsg) {
#ifdef NETWORK //If using network server codes
	char* data;
	if ( !(data = new char[16]) ) {
		printf("%s","Error allocating kernel buffer for saving index!\n");
		return -1;
	} 
	sprintf(data,"%d", cvIndex);
	
	int success = Request(DESTROYCV, data, getMailID()); //Request that the server
	if(success == cvIndex) 
	{
		printf("DestroyCV runs successfully!\n");
		return 0;
	}
	else
	{
		return -1;
	}
#else
    // Destroy a cv from array of user CVs
	destroyCVLock->Acquire();
	int error = 0; //0 - success, 2 - Desired CV does not exist
				   //4 - cvIndex out of bounds, 6 - CV belongs to a different process

	if((cvIndex < 0)||(cvIndex > (MAX_USER_CV-1)))
	{
		if(printMsg)
		{
			printf("Cannot destroy: CV index [%d] out of bounds [%d, %d].\n", cvIndex, 0, (MAX_USER_CV-1));
		}
		error = 4; 
		destroyCVLock->Release();
		return error;
	}

	if(userCV[cvIndex] != NULL)
	{	
		if(cvProcessMap[cvIndex] != currentThread->space)
		{
			error = 6; 
			if(printMsg)
			{
				printf("Cannot destroy: %s belongs to different process.\n", userCV[cvIndex]->getName());
			}
			destroyCVLock->Release();
			return error;
		}

		delete userCV[cvIndex];
		userCV[cvIndex] = NULL;

		//Make sure deleted successfully
		if(userCV[cvIndex]==NULL)
		{
			if(printMsg)
			{
				printf("CV_%d destroyed successfully!\n", cvIndex);
			}
		}
		else
		{
			printf("Error in destruction of CV_%d!\n", cvIndex);
		}
	}
	else
	{
		error = 2; 

		if(printMsg)
		{
			printf("Cannot destroy: CV does not exist at index %d.\n", cvIndex);
		}
	}
	destroyCVLock->Release();
	return error; //Return if failed or not
#endif
}

int CreateMV_Syscall(unsigned int vaddr, int len, int printMsg) {
	char* name;
	if ( !(name = new char[len]) ) {
		printf("%s","Error allocating kernel buffer for saving lock name!\n");
		return -1;
	} 
	else {
		if ( copyin(vaddr,len,name) == -1 ) {
			printf("%s","Bad pointer passed to CreateLock: data not written\n");
			delete[] name;
			return -1;
		}
	}
    // CreateMV
#ifndef NETWORK //If not using server to handle creation
	createMVLock->Acquire();
	//If index is in bounds, create a mv in the mv array
    if((createMVIndex >= 0)&&(createMVIndex <= (MAX_USER_MV-1)))
    {
            char* name = new char [16];
            sprintf(name,"MV_%d",createMVIndex);
            userMV[createMVIndex] = new int;
		
			//Make sure created successfully
			if(userMV[createMVIndex]!=NULL)
			{
				if(printMsg)
				{
					printf("%s created successfully!\n", name);
				}
			}
			createMVLock->Release();
            return createMVIndex++;
    }
    else
    {
		if(printMsg)
		{
            printf("MV cannot be created! Maximum number of MVs [%d] exceeded!\n", MAX_USER_MV);
		}
		createMVLock->Release();
        return -1;
    }
#else
	//printf("Running from network folder!\n");
	int mvIndex = Request(CREATEMV, name, getMailID()); //Request that the server create a lock 
	if(mvIndex > -1) //If valid index. Not checking MAX because that is information decided by server. 
	{
		printf("%s created at index %d successfully or already existed!\n", name, mvIndex); 
		return mvIndex;
	}
	else
	{
		printf("%s NOT created successfully!\n", name);
		return -1;
	}
#endif
}

int SetMV_Syscall(int mvIndex, int value, int printMsg) {
#ifndef NETWORK //If not using server to handle setting mv
    // Set a mv from array of user MVs
	setMVLock->Acquire();
	int error = 0; //0 - success, 1 - Desired mv does not exist
				   //3 - mvIndex out of bounds

	if((mvIndex < 0)||(mvIndex > (MAX_USER_MV-1)))
	{
		error = 3; 
		if(printMsg)
		{
			printf("Cannot set: MV index [%d] out of bounds [%d, %d].\n", mvIndex, 0, (MAX_USER_MV-1));
		}
		setMVLock->Release();
		return error;
	}

	if(userMV[mvIndex]!=NULL)
	{
		*userMV[mvIndex] = value;
	}
	else
	{
		printf("Cannot set: MV index [%d] has not been created.\n", mvIndex);
	}
	setMVLock->Release();
	return error; //Return if failed or not
#else
	char* indValBuf;
	if ( !(indValBuf = new char[16]) ) {
		printf("%s","Error allocating kernel buffer for saving value!\n");
		return -1; 
	} 
	sprintf(indValBuf,"%d_%d", mvIndex, value);
	printf("Running from network folder!\n");
	//printf("Running from network folder!\n");
	int setIndex = Request(SETMV, indValBuf, getMailID()); //Request that the server set mv to value 
	if(setIndex == mvIndex) //If mv set is the one client asked to set
	{
		printf("MV at index %d set successfully!\n", setIndex);
		return 0; //0 - error code for success
	}
	else
	{
		return -1;
	}
#endif
}

int GetMV_Syscall(int mvIndex,int printMsg) {
#ifndef NETWORK //If not using server to handle setting mv
	int error;
    // Get a mv from array of user MVs
	getMVLock->Acquire();
	//Error -1 - Desired mv does not exist or mvIndex out of bounds

	if((mvIndex < 0)||(mvIndex > (MAX_USER_MV-1)))
	{
		error = 3; 
		if(printMsg)
		{
			printf("Cannot get: MV index [%d] out of bounds [%d, %d].\n", mvIndex, 0, (MAX_USER_MV-1));
		}
		getMVLock->Release();
		return -1;
	}

	getMVLock->Release();
	return *userMV[mvIndex];
#else
	char* indexBuf;
	if ( !(indexBuf = new char[16]) ) {
		printf("%s","Error allocating kernel buffer for saving index!\n");
		return -1;
	} 
    
	sprintf(indexBuf,"%d",mvIndex);
	//printf("Running from network folder!\n");
	int getValue = Request(GETMV, indexBuf, getMailID()); //Request that the server aqcuire a lock 
	
	printf("MV at index %d getted successfully!\n", mvIndex); //Should I do some kind of check?
	return getValue; 
#endif
	 
}

int DestroyMV_Syscall(int mvIndex, int printMsg) {
#ifndef NETWORK //If not using server to handle destroying mv
    // Destroy a mv from array of user MVs
	destroyMVLock->Acquire();
	int error = 0; //0 - success, 2 - Desired MV does not exist
				   //4 - mvIndex out of bounds

	if((mvIndex < 0)||(mvIndex > (MAX_USER_MV-1)))
	{
		if(printMsg)
		{
			printf("Cannot destroy: MV index [%d] out of bounds [%d, %d].\n", mvIndex, 0, (MAX_USER_MV-1));
		}
		error = 4; 
		destroyMVLock->Release();
		return error;
	}

	if(userMV[mvIndex] != NULL)
	{	
		delete userMV[mvIndex];
		userMV[mvIndex] = NULL;

		//Make sure deleted successfully
		if(userMV[mvIndex]==NULL)
		{
			if(printMsg)
			{
				printf("MV_%d destroyed successfully!\n", mvIndex);
			}
		}
		else
		{
			printf("Error in destruction of MV_%d!\n", mvIndex);
		}
	}
	else
	{
		error = 2; 

		if(printMsg)
		{
			printf("Cannot destroy: MV does not exist at index %d.\n", mvIndex);
		}
	}
	destroyMVLock->Release();
	return error; //Return if failed or not
#else
	char* indexBuf;
	if ( !(indexBuf = new char[16]) ) {
		printf("%s","Error allocating kernel buffer for saving index!\n");
		return -1;
	} 
    
	sprintf(indexBuf,"%d",mvIndex);
	//printf("Running from network folder!\n");
	int destroyIndex = Request(DESTROYMV, indexBuf, getMailID()); //Request that the server aqcuire a lock 
	
	if(destroyIndex == mvIndex) //If mv set is the one client asked to set
	{
		printf("MV at index %d destroyed successfully!\n", destroyIndex);
		return 0; //0 - error code for success
	}
	else
	{
		return -1;
	} 
#endif
}

void Exec_Thread()
{
	execLock->Acquire();
	//set the initial register values
	currentThread->space->InitRegisters();  
	// load page table register
	currentThread->space->RestoreState(); 
	//Run the machine
	//Reset_TLB();
	execLock->Release();
	machine->Run();
}

SpaceId Exec_Syscall(unsigned int name, int length)
{
	//errors: 0 = no error, 1 = null returned as file descriptor pointer
	execLock->Acquire();
	OpenFile* file = NULL;
	AddrSpace* spaceForNewProcess = NULL;
	Thread* threadForNewProcess = NULL;

	char *newName = new char[length+1];	// Kernel buffer to put the name in

    if( copyin((unsigned int)name,length,newName) == -1 ) 
	{
		printf("%s","Bad pointer passed to Exec\n");
		delete[] newName;
		return -1;
    }
	newName[length]='\0';
	//Initialize the file pointer, to load the code/data from the executable
	file = fileSystem->Open(newName);
	//Cleanup executable character array
	
	if (file != NULL)
	{
		delete [] newName;
		//Allocate address space for new process
		spaceForNewProcess = new AddrSpace(file);
		//Create a thread for that process
		threadForNewProcess = new Thread ("Thread For New Process");
		//Associate new address space with new thread
		threadForNewProcess->space = spaceForNewProcess;
		//Add process to process table
		processTable->Add_Process_Entry(spaceForNewProcess);
		//Assign mail ID to process if not already assigned
		assignMailID(spaceForNewProcess);
		//Internal fork to new process/thread
		execLock->Release();
		threadForNewProcess->Fork((VoidFunctionPtr)Exec_Thread,0);
		return 0; // everything fine
	}
	else
	{
		execLock->Release();
		printf("Bad file name passed to Exec: %s\n", newName);
		delete [] newName;
		return 1;
	}
}

void Kernel_Thread(unsigned int virtualAddress)
{
	forkLock->Acquire();
	//Tells machine where first instruction is
    machine->WriteRegister(PCReg, virtualAddress);	

	//Tells machine where next instruction is (just in case)
   machine->WriteRegister(NextPCReg, virtualAddress+4);

	// Set the stack register to the end of the address space, where we
	// allocated the stack; 
	machine->WriteRegister(StackReg, currentThread->threadStartLoc * PageSize - 16);

	forkLock->Release();
	//Run the machine
	machine->Run();
}

void Fork_Syscall(unsigned int func)
{
	forkLock->Acquire();
	Thread* forkedThread=NULL;
	//First check that we have a possibly valid address
	if ((unsigned int)func > 0 && ((unsigned int)func < PageSize*NumPhysPages)) //PageSize*NumPhysPages=MemorySize in machine.h. Maybe you can use that instead.
	{
		//Create a new thread
		forkedThread = new Thread ("Forked Thread");
		//Allocate stack space for new thread
		forkedThread->threadStartLoc = currentThread->space->AllocateStack();
		//Set newly created thread's address space to process's address space (ie currentThread's)
		forkedThread->space=currentThread->space;
		//Update Process Table to add new thread to current process
		processTable->Add_Thread_Entry(forkedThread, forkedThread->space, forkedThread->threadStartLoc);
		forkLock->Release();
		forkedThread->Fork((VoidFunctionPtr)Kernel_Thread,func);
	}
	else
	{
		forkLock->Release();
		printf("Bad function pointer passed to Fork: %i\n", (unsigned int)func);
	}
}

int Exit_Syscall(int status)
{
	printf ("Exit call: %i\n\n", status);
	exitLock->Acquire();
	bool mainThread=true;
	Reset_TLB();
	ipt->DeallocateThread(currentThread->space, currentThread->threadStartLoc);
	// status: 0 = thread is last userprog thread; 1 = thread is last in its process but not last userprog thread; !0 and !1 = thread is not last in process
	//return : 0 = thread is last userprog thread; 1 = thread is last in its process but not last userprog thread; 2 = thread is not last in process; 3 = problem
	
	if(status == 0)
	{
		if (processTable->Get_Num_Threads()<=1 && processTable->Get_Num_Proccesses()==1) //if thread is last userprog thread
		{
			//Last thing, this time halt OS
			exitLock->Release();
			interrupt->Halt();
			return 0;
		}
		else if(processTable->Is_Last_Thread_In_Process(currentThread->space)) //if thread is last in its process but not last userprog thread
		{
			ipt->DeallocateSpace(currentThread->space);
			if (processTable->Get_Process_Id(currentThread->space) !=0)
			{
				mainThread=false;
			}
			//remove process entry
			//processTable->Remove_Thread_Entry(currentThread);
			//processTable->Remove_Process_Entry(currentThread->space);
			//delete address space
			//Last thing, call finish
			currentThread->space->FreeSwap();
			//if (!mainThread)
			//{
				//printf("not main thread\n");
				
				delete currentThread->space;
				currentThread->Finish();
			//}
			exitLock->Release();
			return 1;
		}
		else //if thread is not last in process 
		{
			//remove thread entry
			processTable->Remove_Thread_Entry(currentThread);
			//Last thing, call finish
			currentThread->Finish();
			exitLock->Release();
			return 2;
		}
	}
	else if (status == 1)
	{
		if (processTable->Get_Num_Threads()<=1 && processTable->Get_Num_Proccesses()==1) //if thread is last userprog thread
		{
			//Last thing, this time halt OS
			exitLock->Release();
			interrupt->Halt();
			return 0;
		}
		else if(processTable->Is_Last_Thread_In_Process(currentThread->space)) //if thread is last in its process but not last userprog thread
		{
			ipt->DeallocateSpace(currentThread->space);
			if (processTable->Get_Process_Id(currentThread->space) !=0)
			{
				mainThread=false;
			}
			//remove process entry
			processTable->Remove_Process_Entry(currentThread->space);
			//delete address space
			//Last thing, call finish
			currentThread->space->FreeSwap();
			if (!mainThread)
			{
				processTable->Remove_Thread_Entry(currentThread);
				delete currentThread->space;
				currentThread->Finish();
		}
			exitLock->Release();
			return 1;
		}
		else //if thread is not last in process 
		{
			//remove thread entry
			processTable->Remove_Thread_Entry(currentThread);
			//Last thing, call finish
			currentThread->Finish();
			exitLock->Release();
			return 2;
		}
	}
	else if (status != 1 && status != 0)
	{
		if (processTable->Get_Num_Threads()<=1 && processTable->Get_Num_Proccesses()==1) //if thread is last userprog thread
		{
			//Last thing, this time halt OS
			interrupt->Halt();
			exitLock->Release();
			return 0;
		}
		else if(processTable->Is_Last_Thread_In_Process(currentThread->space)) //if thread is last in its process but not last userprog thread
		{
			ipt->DeallocateSpace(currentThread->space);
			if (processTable->Get_Process_Id(currentThread->space) !=0)
			{
				mainThread=false;
			}
			
			//remove process entry
			//processTable->Remove_Process_Entry(currentThread->space);
			//delete address space
			//Last thing, call finish
			currentThread->space->FreeSwap();
			if (!mainThread)
			{
				processTable->Remove_Thread_Entry(currentThread);
				delete currentThread->space;
				currentThread->Finish();
			}
			exitLock->Release();
			return 1;
		}
		else //if thread is not last in process 
		{
			//remove thread entry
			processTable->Remove_Thread_Entry(currentThread);
			//Last thing, call finish
			currentThread->Finish();
			exitLock->Release();
			return 2;
		}
	}
	else
	{
		printf("Why are we here???\n\n");
		exitLock->Release();
	}
	return 3;
}

/*Printing out strings*/
void PrintOut_Syscall(unsigned int str, int len){
	Write_Syscall(str,len,1);
}

/*generate a random interger number*/
int RandomInt_Syscall(){
	return Random(); 
}

void PageFaultHandler(int virtualAddr)
{
	int vpn = virtualAddr/PageSize;
	int physicalPageNum = 0;
	int pageToEvict=0;
	int pageToEvictProcess=0;
	int swapFilePageLocation=0;
	int location=-1;
	int processId=0;
	AddrSpace* evictionSpace=NULL;
	bool random = randomEviction;

	if (vpn>=0 && vpn < currentThread->space->isMaxSize()) // not sure what upper bound is supposed to be beyond limit of unsigned int
	{
		location = currentThread->space->GetLocation(vpn);
		processId = processTable->Get_Process_Id(currentThread->space);
		tlbLock->Acquire();
		for (int i = 0; i<TLBSize; i++)
		{
			if(machine->tlb[i].valid && machine->tlb[i].dirty)
				ipt->MakeDirty(machine->tlb[i].physicalPage);
		}	
		tlbLock->Release();
		if (processId == -1  && processTable->Get_Num_Threads()>0)
		{
			printf("Invalid process Id.  Unrecognized thread trying to run in the system.  Halting\n\n");
			interrupt->Halt();
		}
		if (location == 0) //already in memory
		{
			ipt->PopulateTLBSearch(vpn,processId);  //new addrspace method to populate TLB
			tlbLock->Acquire();
			tlbCounter++;
			if (tlbCounter==4)
				tlbCounter = 0; //tlbcounter ranges from [0,3]
			tlbLock->Release();
		}
		else if (location == 1) //it's in the executable, it's not in memory, and there is now saved version in swap
		{
			physicalPageNum=iptCheck->Find();
			if (physicalPageNum != -1) //free page of memory
			{
				//Addrspace method to load page from exec into physical page num
				currentThread->space->LoadFromExec(vpn,physicalPageNum);		
				ipt->Assign(physicalPageNum, vpn, processId); //processId not being implemented yet so 0 for placeholder
				ipt->PopulateTLB(physicalPageNum);  //new addrspace method to populate TLB
				tlbLock->Acquire();
				tlbCounter++;
				if (tlbCounter==4)
					tlbCounter = 0; //tlbcounter ranges from [0,3]
				tlbLock->Release();
			}			
			else
			{	
				//DEPENDS ON EVICTION ALGORITHM
				if(random) // -P RAND  at commandline
				{
					//Randomly select a page
					execAccessLock->Acquire();
					pageToEvict = Random()%(NumPhysPages);
					if (globalEviction != -1)
					{
						while(pageToEvict==globalEviction)
							pageToEvict = Random()%(NumPhysPages);
					}
					globalEviction=pageToEvict;
					execAccessLock->Release();
					pageToEvictProcess = ipt->GetId(pageToEvict);
					evictionSpace=processTable->Get_AddressSpace(pageToEvictProcess);
					if(evictionSpace == NULL)
					{
						printf("couldn't find record\n");
						evictionSpace = currentThread->space;
					}
					tlbLock->Acquire();
					for (int i = 0; i<TLBSize; i++)
					{
						if(machine->tlb[i].physicalPage == pageToEvict)
						{
							machine->tlb[i].valid = FALSE;
							if (machine->tlb[i].dirty)
								ipt->MakeDirty(machine->tlb[i].physicalPage);
						}	
					}
					tlbLock->Release();
					if(ipt->isDirty(pageToEvict)) //IPT method
					{
						// find a free page in swap, save to swap file and update addrspace
						swapAccessLock->Acquire();
						swapFilePageLocation = swapFileCheck->Find(); //find a free page in swap file
						if (swapFilePageLocation == -1)
						{
							printf("Swap file ran out of space.  OS aborting.\n\n");
							interrupt->Halt();
						}
						swapFile->WriteAt(&(machine->mainMemory[pageToEvict*PageSize]), PageSize, swapFilePageLocation*PageSize); //write entry into swap
						swapAccessLock->Release();
						evictionSpace->MovedToSwap(ipt->GetVirtual(pageToEvict),swapFilePageLocation);
						ipt->Update(pageToEvict, vpn, processId); //update ipt table				
						currentThread->space->LoadFromExec(vpn,pageToEvict); //update page table and load code/data into memory at pageToEvict		
					}
					else // it's not dirty
					{
						//don't copy to swap; clear that space in the physical page bitmap
						evictionSpace->Evict(ipt->GetVirtual(pageToEvict));
						ipt->Update(pageToEvict, vpn, processId); //update ipt table
						currentThread->space->LoadFromExec(vpn,pageToEvict); //update page table and load code/data into memory at pageToEvict
					}
				}
				else //doing FIFO eviction
				{
					//stuff for FIFO eviction
					pageToEvict = ipt->FIFO();
					pageToEvictProcess = ipt->GetId(pageToEvict);
					evictionSpace=processTable->Get_AddressSpace(pageToEvictProcess);
					if(evictionSpace == NULL)
						evictionSpace = currentThread->space;
					tlbLock->Acquire();
					for (int i = 0; i<TLBSize; i++)
					{
						if(machine->tlb[i].physicalPage == pageToEvict)
						{
							machine->tlb[i].valid = FALSE;
							if (machine->tlb[i].dirty)
								ipt->MakeDirty(machine->tlb[i].physicalPage);
						}
					}
					tlbLock->Release();
					if(ipt->isDirty(pageToEvict)) //IPT method
					{
						
						// find a free page in swap, save to swap file and update addrspace
						swapAccessLock->Acquire();
						swapFilePageLocation = swapFileCheck->Find(); //find a free page in swap file
						if (swapFilePageLocation == -1)
						{
							printf("Swap file ran out of space.  OS aborting.\n\n");
							interrupt->Halt();
						}
						swapFile->WriteAt(&(machine->mainMemory[pageToEvict*PageSize]), PageSize, swapFilePageLocation*PageSize); //write entry into swap
						swapAccessLock->Release();
						evictionSpace->MovedToSwap(ipt->GetVirtual(pageToEvict),swapFilePageLocation);
						ipt->Update(pageToEvict, vpn, processId); //update ipt table
						currentThread->space->LoadFromExec(vpn,pageToEvict); //update page table and load code/data into memory at pageToEvict			
					}
					else // it's not dirty
					{
						//don't copy to swap; clear that space in the physical page bitmap
						evictionSpace->Evict(ipt->GetVirtual(pageToEvict));
						ipt->Update(pageToEvict, vpn, processId); //update ipt table
						currentThread->space->LoadFromExec(vpn,pageToEvict); //update page table and load code/data into memory at pageToEvict	
					}
				}			
				ipt->PopulateTLB(pageToEvict);  //new addrspace method to populate TLB
				tlbLock->Acquire();
				tlbCounter++;
				if (tlbCounter==4)
					tlbCounter = 0; //tlbcounter ranges from [0,3]
				tlbLock->Release();
				globalEviction == -1;
			}
		}
		else if (location == 2)//it's in swap
		{
			physicalPageNum=iptCheck->Find();
			if (physicalPageNum != -1) //free page of memory
			{
				//Addrspace method to load page from swap into physical page num
				currentThread->space->LoadFromSwap(vpn,physicalPageNum);
				ipt->Assign(physicalPageNum, vpn, processId); //processId not being implemented yet so 0 for placeholder
				ipt->PopulateTLB(physicalPageNum);  //new addrspace method to return entry of pagetable
				tlbLock->Acquire();
				tlbCounter++;
				if (tlbCounter==4)
					tlbCounter = 0; //tlbcounter ranges from [0,3]
				tlbLock->Release();
			}
			else
			{
				if(random) //-P RAND  at commandline
				{
					//Randomly select a page
					execAccessLock->Acquire();
					pageToEvict = Random()%(NumPhysPages);
					if (globalEviction != -1)
					{
						while(pageToEvict==globalEviction)
							pageToEvict = Random()%(NumPhysPages);
					}
					globalEviction=pageToEvict;
					execAccessLock->Release();
					pageToEvictProcess = ipt->GetId(pageToEvict);
					evictionSpace=processTable->Get_AddressSpace(pageToEvictProcess);
					if(evictionSpace == NULL)
						evictionSpace = currentThread->space;
					tlbLock->Acquire();
					for (int i = 0; i<TLBSize; i++)
					{
						if(machine->tlb[i].physicalPage == pageToEvict)
						{
							machine->tlb[i].valid = FALSE;
							if (machine->tlb[i].dirty)
								ipt->MakeDirty(machine->tlb[i].physicalPage);
						}
					}
					tlbLock->Release();
					if(ipt->isDirty(pageToEvict)) //IPT method
					{
						// find a free page in swap, save to swap file and update addrspace
						swapAccessLock->Acquire();
						swapFilePageLocation = swapFileCheck->Find(); //find a free page in swap file
						if (swapFilePageLocation == -1)
						{
							printf("Swap file ran out of space.  OS aborting.\n\n");
							interrupt->Halt();
						}
						swapFile->WriteAt(&(machine->mainMemory[pageToEvict*PageSize]), PageSize, swapFilePageLocation*PageSize); //write entry into swap
						swapAccessLock->Release();
						evictionSpace->MovedToSwap(ipt->GetVirtual(pageToEvict),swapFilePageLocation);
						ipt->Update(pageToEvict, vpn, processId); //update ipt table
						currentThread->space->LoadFromSwap(vpn,pageToEvict); //update page table and load code/data into memory at pageToEvict
					}
					else // it's not dirty
					{
						//don't copy to swap; clear that space in the physical page bitmap
						evictionSpace->Evict(ipt->GetVirtual(pageToEvict));
						ipt->Update(pageToEvict, vpn, processId); //update ipt table
						currentThread->space->LoadFromSwap(vpn,pageToEvict); //update page table and load code/data into memory at pageToEvict	
					}
				}
				else //doing FIFO eviction
				{
					//stuff for FIFO eviction
					pageToEvict = ipt->FIFO();
					pageToEvictProcess = ipt->GetId(pageToEvict);
					evictionSpace=processTable->Get_AddressSpace(pageToEvictProcess);
					if(evictionSpace == NULL)
						evictionSpace = currentThread->space;
					tlbLock->Acquire();
					for (int i = 0; i<TLBSize; i++)
					{
						if(machine->tlb[i].physicalPage == pageToEvict)
						{
							machine->tlb[i].valid = FALSE;
							if (machine->tlb[i].dirty)				
								ipt->MakeDirty(machine->tlb[i].physicalPage);
						}
					}
					tlbLock->Release();
					if(ipt->isDirty(pageToEvict)) //IPT method
					{
						// find a free page in swap, save to swap file and update addrspace
						swapAccessLock->Acquire();
						swapFilePageLocation = swapFileCheck->Find(); //find a free page in swap file
						if (swapFilePageLocation == -1)
						{
							printf("Swap file ran out of space.  OS aborting.\n\n");
							interrupt->Halt();
						}
						swapFile->WriteAt(&(machine->mainMemory[pageToEvict*PageSize]), PageSize, swapFilePageLocation*PageSize); //write entry into swap
						swapAccessLock->Release();
						evictionSpace->MovedToSwap(ipt->GetVirtual(pageToEvict),swapFilePageLocation);
						ipt->Update(pageToEvict, vpn, processId); //update ipt table
						currentThread->space->LoadFromSwap(vpn,pageToEvict); //update page table and load code/data into memory at pageToEvict
					}
					else // it's not dirty
					{
						//don't copy to swap; clear that space in the physical page bitmap
						evictionSpace->Evict(ipt->GetVirtual(pageToEvict));
						ipt->Update(pageToEvict, vpn, processId); //update ipt table
						currentThread->space->LoadFromSwap(vpn,pageToEvict); //update page table and load code/data into memory at pageToEvict	
					}
				}	
				ipt->PopulateTLB(pageToEvict);  //new addrspace method to populate TLB
				tlbLock->Acquire();
				tlbCounter++;
				if (tlbCounter==4)
					tlbCounter = 0; //tlbcounter ranges from [0,3]
				tlbLock->Release();
				globalEviction = -1;
			}
		}	
		else //stack space that hasn't been initialized
		{
			physicalPageNum=iptCheck->Find();
			if (physicalPageNum != -1) //free page of memory
			{
				//Addrspace method to load page from swap into physical page num
				currentThread->space->InitializeStack(vpn);
				ipt->Assign(physicalPageNum, vpn, processId); //processId not being implemented yet so 0 for placeholder
				ipt->PopulateTLB(physicalPageNum);  //new addrspace method to return entry of pagetable
				tlbLock->Acquire();
				tlbCounter++;
				if (tlbCounter==4)
					tlbCounter = 0; //tlbcounter ranges from [0,3]
				tlbLock->Release();
			}
			else
			{
				if(random) //-P RAND  at commandline
				{
					//Randomly select a page
					execAccessLock->Acquire();
					pageToEvict = Random()%(NumPhysPages);
					if (globalEviction != -1)
					{
						while(pageToEvict==globalEviction)
							pageToEvict = Random()%(NumPhysPages);
					}
					globalEviction=pageToEvict;
					execAccessLock->Release();
					pageToEvictProcess = ipt->GetId(pageToEvict);
					evictionSpace=processTable->Get_AddressSpace(pageToEvictProcess);
					if(evictionSpace == NULL)
						evictionSpace = currentThread->space;
					tlbLock->Acquire();
					for (int i = 0; i<TLBSize; i++)
					{
						if(machine->tlb[i].physicalPage == pageToEvict)
						{
							machine->tlb[i].valid = FALSE;
							if (machine->tlb[i].dirty)
								ipt->MakeDirty(machine->tlb[i].physicalPage);	
						}
					}	
					tlbLock->Release();
					if(ipt->isDirty(pageToEvict)) //IPT method
					{
						// find a free page in swap, save to swap file and update addrspace
						swapAccessLock->Acquire();
						swapFilePageLocation = swapFileCheck->Find(); //find a free page in swap file
						if (swapFilePageLocation == -1)
						{
							printf("Swap file ran out of space.  OS aborting.\n\n");
							interrupt->Halt();
						}
						swapFile->WriteAt(&(machine->mainMemory[pageToEvict*PageSize]), PageSize, swapFilePageLocation*PageSize); //write entry into swap
						swapAccessLock->Release();
						evictionSpace->MovedToSwap(ipt->GetVirtual(pageToEvict),swapFilePageLocation);
						ipt->Update(pageToEvict, vpn, processId); //update ipt table
					}
					else // it's not dirty
					{
						//don't copy to swap; clear that space in the physical page bitmap
						evictionSpace->Evict(ipt->GetVirtual(pageToEvict));
						ipt->Update(pageToEvict, vpn, processId); //update ipt table
						
					}
				}
				else //doing FIFO eviction
				{
					//stuff for FIFO eviction
					pageToEvict = ipt->FIFO();
					pageToEvictProcess = ipt->GetId(pageToEvict);
					evictionSpace=processTable->Get_AddressSpace(pageToEvictProcess);
					if(evictionSpace == NULL)
						evictionSpace = currentThread->space;
					tlbLock->Acquire();
					for (int i = 0; i<TLBSize; i++)
					{
						if(machine->tlb[i].physicalPage == pageToEvict)
						{
							machine->tlb[i].valid = FALSE;
							if (machine->tlb[i].dirty)
								ipt->MakeDirty(machine->tlb[i].physicalPage);
						}
					}
					tlbLock->Release();
					if(ipt->isDirty(pageToEvict)) //IPT method
					{
						// find a free page in swap, save to swap file and update addrspace
						swapAccessLock->Acquire();
						swapFilePageLocation = swapFileCheck->Find(); //find a free page in swap file
						if (swapFilePageLocation == -1)
						{
							printf("Swap file ran out of space.  OS aborting.\n\n");
							interrupt->Halt();
						}
						swapFile->WriteAt(&(machine->mainMemory[pageToEvict*PageSize]), PageSize, swapFilePageLocation*PageSize); //write entry into swap
						swapAccessLock->Release();
						evictionSpace->MovedToSwap(ipt->GetVirtual(pageToEvict),swapFilePageLocation);
						ipt->Update(pageToEvict, vpn, processId); //update ipt table
					}
					else // it's not dirty
					{
						//don't copy to swap; clear that space in the physical page bitmap
						evictionSpace->Evict(ipt->GetVirtual(pageToEvict));
						ipt->Update(pageToEvict, vpn, processId); //update ipt table
					}
				}			
				ipt->PopulateTLB(pageToEvict);  //new addrspace method to populate TLB
				currentThread->space->InitializeStack(vpn);
				tlbLock->Acquire();
				tlbCounter++;
				if (tlbCounter==4)
					tlbCounter = 0; //tlbcounter ranges from [0,3]
				tlbLock->Release();
				globalEviction = -1;
			}
		}
	}
	else
	{
		printf("Bad virtual page number: %i.  Out of bounds.  Aborting OS.\n\n", vpn);
		interrupt->Halt();
	}
}

void assignMailID(AddrSpace* spaceID)
{
	if(procIDIndex >= 10)
	{
		printf("Max number of processes per Nachos instance exceeded!!!\n");
		return;
	}

	for(int i = 0; i < 10; i++)
	{
		if(processIDs[i] == spaceID)
		{
			printf("Process already in instance process array!\n");
			return; //Already in table
		}
	}

	processIDs[procIDIndex++] = spaceID;
	return;
}

int getMailID()
{
	for(int i = 0; i < 10; i++)
	{
		if(processIDs[i] == currentThread->space)
		{
			printf("Returning mailID of %d\n",i);
			return i;//1; //Use index as mail ID
		}
	}
	return 999;
}

#endif

void ExceptionHandler(ExceptionType which) {
    int type = machine->ReadRegister(2); // Which syscall?
    int rv=0; 	// the return value from a syscall

    if ( which == SyscallException ) {
	switch (type) {
	    default:
		DEBUG('a', "Unknown syscall - shutting down.\n");
	    case SC_Halt:
		DEBUG('a', "Shutdown, initiated by user program.\n");
		interrupt->Halt();
		break;
	    case SC_Create:
		DEBUG('a', "Create syscall.\n");
		Create_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
		break;
	    case SC_Open:
		DEBUG('a', "Open syscall.\n");
		rv = Open_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
		break;
	    case SC_Write:
		DEBUG('a', "Write syscall.\n");
		Write_Syscall(machine->ReadRegister(4),
			      machine->ReadRegister(5),
			      machine->ReadRegister(6));
		break;
	    case SC_Read:
		DEBUG('a', "Read syscall.\n");
		rv = Read_Syscall(machine->ReadRegister(4),
			      machine->ReadRegister(5),
			      machine->ReadRegister(6));
		break;
	    case SC_Close:
		DEBUG('a', "Close syscall.\n");
		Close_Syscall(machine->ReadRegister(4));
		break;
#ifdef CHANGED
		case SC_Yield:
		DEBUG('a', "Yield syscall.\n");
		Yield_Syscall(machine->ReadRegister(4));
		break;
		case SC_CreateLock:
		DEBUG('a', "CreateLock syscall.\n");
		rv = CreateLock_Syscall(machine->ReadRegister(4), machine->ReadRegister(5), machine->ReadRegister(6));
		break;
		case SC_Acquire:
		DEBUG('a', "Acquire syscall.\n");
		rv = Acquire_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
		break;
		case SC_Release:
		DEBUG('a', "Release syscall.\n");
		rv = Release_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
		break;
		case SC_DestroyLock:
		DEBUG('a', "Release syscall.\n");
		rv = DestroyLock_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
		break;
		case SC_CreateCV:
		DEBUG('a', "CreateCV syscall.\n");
		rv = CreateCV_Syscall(machine->ReadRegister(4),machine->ReadRegister(5),machine->ReadRegister(6));
		break;
		case SC_Signal:
		DEBUG('a', "Signal syscall.\n");
		rv = Signal_Syscall(machine->ReadRegister(4), machine->ReadRegister(5), machine->ReadRegister(6));
		break;
		case SC_Wait:
		DEBUG('a', "Wait syscall.\n");
		rv = Wait_Syscall(machine->ReadRegister(4), machine->ReadRegister(5), machine->ReadRegister(6));
		break;
		case SC_Broadcast:
		DEBUG('a', "Broadcast syscall.\n");
		rv = Broadcast_Syscall(machine->ReadRegister(4), machine->ReadRegister(5), machine->ReadRegister(6));
		break;
		case SC_DestroyCV:
		DEBUG('a', "DestroyCV syscall.\n");
		rv = DestroyCV_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
		break;
		case SC_Exec:
			DEBUG('a', "Execute Thread.\n");
			Exec_Syscall(machine->ReadRegister(4),machine->ReadRegister(5));
			break;
		case SC_Fork:
			DEBUG('a', "Fork Thread.\n");
			Fork_Syscall((unsigned int)machine->ReadRegister(4));
			break;
		case SC_Exit:
			DEBUG('a', "Execute Thread.\n");
			rv=Exit_Syscall(machine->ReadRegister(4));
			//currentThread->Finish();
			break;
		case SC_PrintOut:
			DEBUG('a', "PrintOut Syscall.\n");
			PrintOut_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
			break;
		case SC_RandomInt:
			DEBUG('a', "RandomInt Syscall.\n");
			rv = RandomInt_Syscall();
			break;
		case SC_CheckLock:
			DEBUG('a', "CheckLock Syscall.\n");
			rv = CheckLock_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
			break;
		case SC_CreateMV:
			DEBUG('a', "CreateMV Syscall.\n");
			rv = CreateMV_Syscall(machine->ReadRegister(4), machine->ReadRegister(5), machine->ReadRegister(6));
			break;
		case SC_SetMV:
			DEBUG('a', "SetMV Syscall.\n");
			rv = SetMV_Syscall(machine->ReadRegister(4), machine->ReadRegister(5), machine->ReadRegister(6));
			break;
		case SC_GetMV:
			DEBUG('a', "GetMV Syscall.\n");
			rv = GetMV_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
			break;
		case SC_DestroyMV:
			DEBUG('a', "DestroyMV Syscall.\n");
			rv = DestroyMV_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
			break;
	
#endif
	}

	// Put in the return value and increment the PC
	machine->WriteRegister(2,rv);
	machine->WriteRegister(PrevPCReg,machine->ReadRegister(PCReg));
	machine->WriteRegister(PCReg,machine->ReadRegister(NextPCReg));
	machine->WriteRegister(NextPCReg,machine->ReadRegister(PCReg)+4);
	return;
    } 
	else if (which == PageFaultException)
		PageFaultHandler(machine->ReadRegister(39));
	else {
      cout<<"Unexpected user mode exception - which:"<<which<<"  type:"<< type<<endl;
      interrupt->Halt();
    }
}
